"""
This module contains the Console class used to redirect the output
stream from the Landis-II application along with the interface
to specify the Console class.
"""
"""
   Copyright 2011 Gabriel Elkind and Brian Gapinski

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
"""
import re
import logging

import wx

logger = logging.getLogger('console')
class ConsoleInterface(object):
    """
    This class serves as the display for the output stream of Landis
    """
    def __init__(self, lime):
        """
        Constructs the console object

        @type lime: Lime
        @param lime: The GUI container and parent of this object
        """
        pass

    def Notify(self, text):
        """
        Notifies the console that a change has occured in the model

        @type text: string
        @param text: Output stream to append to the console
        """
        raise NotImplementedError

    def SetModel(self, model):
        """
        Sets the model for the console

        @type model: Model
        @param model: The new model to be set
        """
        raise NotImplementedError
    
    def AppendError(self, error):
        """
        Handles errors passed by the landis-ii application

        @type error: string
        @param error: The error outputted by lime
        """
        raise NotImplementedError


class Console(wx.TextCtrl, ConsoleInterface):
    """
    This module serves as the display for the Landis-II application.
    Output text and errors can be appended to the display. In the case
    of errors, an event is created so that the user can click on the
    underlined text to bring up the relevant file.

    @type _lime: Lime
    @ivar _lime: The parent of the console object

    @type _error: string
    @ivar _error: Holds any error messages passed through by Landis-II
    """
    def __init__(self, lime, *args, **kwargs):
        """
        This module receives the output from a Landis-II application
        and displays the text. Need to add the functionality to
        select errors and open the corresponding file.

        @param lime: The parent of this console object
        @type lime: Lime class
        """
        logger.debug('Console created')
        self._lime = lime
        self._process = self._lime.GetProcess()
        # The style of the console textctrl
        tc = wx.TE_MULTILINE|wx.TE_READONLY
        wx.TextCtrl.__init__(self, lime, style=tc, size=(600,200),\
            *args, **kwargs)
        
        self._error = ''
        self._model = None

        # The default font for the console
        style = wx.TextAttr()
        font = wx.Font(10, wx.FONTFAMILY_TELETYPE, wx.NORMAL, wx.NORMAL, False)
        style.SetFont(font)
        self.SetDefaultStyle(style)

        self._process.Register(self)

    def Notify(self, text):
        """
        Informs this object that a file has
        changed within the central model
        
        @param text: The output stream to append to the console
        @type text: string
        """
        if 'Error' in text:
            error = text.find('Error')
            self.AppendText(text[:error])
            self.AppendError(text[error:])
            self._process.SetError(True)
        else:
            self.AppendText(text)

    def SetModel(self,model):
        """
        Sets the model for the console

        @type model: Model
        @param model: The new model to be set
        """
        self._model = model
    
    def AppendError(self, error):
        """
        This function appends an error text to the output display. The
        error message is underlined and can be selected to bring up
        the location of the error.

        @type error: string
        @param error: The error message to be appended
        """
        logger.debug('Appending error text')
        # Adds space from the normal output
        self.AppendText('\n')

        # Gets the start and end points of the error text to change style
        start = self.GetLastPosition()
        self.AppendText(error)
        end = self.GetLastPosition()
        
        # Creates the new style to apply to the error message
        style = wx.TextAttr()
        font = wx.Font(10, wx.TELETYPE, wx.NORMAL, wx.NORMAL, True)
        style.SetFont(font)
        self.SetStyle(start, end, style)
        self._error = error
        logger.debug('Error message: %s', self._error)

        # Binds the error to click event
        ### Still needs to be fixed. Currently clicking the window activates
        ### the event. Looking for a way to bind to a string
        self.Bind(wx.EVT_LEFT_DOWN, self._OnErrorClick, self)

    # Maybe unbind after the user has clicked this error
    def _OnErrorClick(self, event):
        """
        Signals that the user wishes to view the source of the error
        """
        logger.debug('Entering _OnErrorClick')
        # Gets the file that generated the error
        pattern = '\w*.txt'
        filename = re.search(pattern, self._error)
        filename = filename.group(0)
        logger.debug('File containing error: %s', filename)

        # Gets the line number of the error
        pattern = '\d+'
        line = re.search(pattern, self._error)
        line = int(line.group(0))
        logger.debug('Line containing error: %i', line)

        # Gets the actual error
        pattern = '"\w*"'
        error = re.findall(pattern, self._error)
        error = error[0][1:-1] # Trim quotes
        logger.debug('Error message: %s', error)

        self._lime.OpenError(filename, line, error)
